#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>
#include "dbm/dbus-monitor.h"

typedef struct epoll_event EPEV;

static int epfd = -1;
static EPEV epevs[DBM_MAX_WATCHES+1];
static dbus_bool_t add_watch(DBusWatch *w, void *data);
static void remove_watch(DBusWatch *w, void *data);
static void dispatch(DBusMonitor *m);
static void handleDBusEvent(DBusMonitor *m, EPEV *e);
static dbus_bool_t procCtlFD(DBusMonitor *m, EPEV *e);

int main(int argc, char *argv[])
{
    const char *rules[] = {
        "path=/org/gnome/SettingsDaemon/Rfkill",
        "path=/StatusNotifierItem", NULL //end flag
    };
    epfd = epoll_create1(0);
    DBusMonitor *m = dbmCreateWithType(DBUS_BUS_SESSION, add_watch,
                                       remove_watch, dispatch);
    dbmInitConnection(m);
    dbmAddHandler(m, dbmDefaultHandler, m, NULL);
    dbmAddRules(m, rules);
    dbmStart(m);
    sleep(16);
    dbmStop(m);
    dbmDestroy(m);
    close(epfd);
    return 0;
} 

dbus_bool_t add_watch(DBusWatch *w, void *data) {
    EPEV e;
    uint32_t etype = 0;
    if (!dbus_watch_get_enabled(w))
        return TRUE;
    e.data.ptr = w;
    unsigned int flags = dbus_watch_get_flags(w);
    if(flags & DBUS_WATCH_READABLE) etype |= EPOLLIN;
    if(flags & DBUS_WATCH_WRITABLE) etype |= EPOLLOUT;
    e.events = etype;
    
    int fd = dbus_watch_get_unix_fd(w);
    
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &e)) {
        perror("DBusMonitor add DBusWatch failed");
        return FALSE;
    }
    return TRUE;
}

void remove_watch(DBusWatch *w, void *data) {
    EPEV e;
    int fd = dbus_watch_get_unix_fd(w);
    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &e);
    printf("removed dbus watch watch=%p\n", w);
}


dbus_bool_t procCtlFD(DBusMonitor *m, EPEV *e) {
    DBMCtrl cmd = -1;
    if(e->events & EPOLLIN) {
        read(e->data.fd, &cmd, sizeof(DBMCtrl));
        if(cmd == DBMSTOP) {
            return FALSE;
        }else {
            fprintf(stderr, "Unsupported DBusMonitor control cmd: %d!\n",
                    cmd);
        }
    }
    if(e->events & EPOLLERR ) {
        fprintf(stderr, "DBusMonitor control pipe broken!\n");
        return FALSE;
    }
    return TRUE;
}

void dispatch(DBusMonitor *m) {
    EPEV e;
    int n;
    int i;
    int cfd = dbmGetCtrlFd(m); //ctrl fd
    e.data.ptr = NULL;
    e.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &e);
    REPEAT:
    n = epoll_wait(epfd, epevs, DBM_MAX_WATCHES+1, -1);
    if(n<0) {
        perror("DBusMonitor mainloop execption");
        goto OUT;
    }       
    for(i=0; i<n; i++) {
        if(epevs[i].data.ptr == NULL) {
            epevs[i].data.fd = cfd;
            if(!procCtlFD(m, &epevs[i]))
                goto OUT;
          
        }else {
            handleDBusEvent(m, &epevs[i]);
        }
        
    }
    goto REPEAT;
    OUT:
    dbmCloseConnection(m);
}


void handleDBusEvent(DBusMonitor *m, EPEV *e) {
    DBusWatch *w = e->data.ptr;
    unsigned int flags = 0;
    if(e->events & EPOLLIN) {
        flags |= DBUS_WATCH_READABLE;
    }
    if(e->events & EPOLLOUT) {
        flags |= DBUS_WATCH_WRITABLE;
        fprintf(stderr, "function:%s, line:%d, file:%s\n", 
                __func__, __LINE__, __FILE__);
    }
    if(e->events & EPOLLHUP) {
        flags |= DBUS_WATCH_HANGUP;
        fprintf(stderr, "function:%s, line:%d, file:%s\n", 
                __func__, __LINE__, __FILE__);
    }
    if(e->events & EPOLLERR) {
        flags |= DBUS_WATCH_ERROR;
        fprintf(stderr, "function:%s, line:%d, file:%s\n", 
                __func__, __LINE__, __FILE__);
    }
    dbmProcessDBusEvent(m, w, flags);
}
